import numpy as np
import matplotlib.pyplot as plt
# from sklearn.preprocessing import OneHotEncoder
#线性模型
def model(x,theta):
    return x.dot(theta)
#sigmoid函数 激活函数
def sigmoid(z):
    return 1/(1+np.exp(-z))
#代价函数
def cost(h,y):
    m=len(h)
    return -1/m*np.sum(y*np.log(h)+(1-y)*np.log(1-h))
#前向传播
def FP(x,theta1,theta2,theta3):
    # 输入层，特征
    a1=x
    #第二层的线性组合  第一个隐藏层的线性组合
    z2=model(a1,theta1)
    #第二层的输出值
    a2=sigmoid(z2)
    z3=model(a2,theta2)
    a3=sigmoid(z3)
    z4=model(a3,theta3)
    # a4 预测值，第四层的输出值
    a4=sigmoid(z4)
    return a2,a3,a4
#反向传播
def BP(x,y,theta1,theta2,theta3,a2,a3,a4,alpha):
    # 第四层的误差
    s4=a4-y
    #第三层的误差
    s3=s4.dot(theta3.T)*(a3*(1-a3))
    # 第二层的误差
    s2=s3.dot(theta2.T)*(a2*(1-a2))

    m=len(x)
    #theta3的梯度
    dt3=1/m*a3.T.dot(s4)
    # theta2的梯度
    dt2=1/m*a2.T.dot(s3)
    #theta1的梯度
    dt1=1/m*x.T.dot(s2)
    #更新参数 梯度下降
    theta3-=alpha*dt3
    theta2-=alpha*dt2
    theta1-=alpha*dt1
    return theta1,theta2,theta3
#梯度下降
def grad(x,y,alpha=0.2,iter0=500):
    m,n=x.shape
    #创建初始theta
    #100：第一个隐藏层神经元个数
    #150：第二个隐藏层神经元个数
    #10：输出层神经元个数，
    #代表分几类
    theta1=np.random.randn(n,100)
    theta2=np.random.randn(100,150)
    theta3=np.random.randn(150,10)
    #存放代价函数的数组
    J=np.zeros(iter0)
    for i in range(iter0):
        #前向传播   算出  a2,a3,a4
        a2, a3, a4=FP(x,theta1,theta2,theta3)
        #存放代价函数值
        J[i]=cost(a4,y)
        #反向传播  更新theta1,theta2,theta3
        theta1, theta2, theta3=BP(x,y,theta1,theta2,theta3,a2,a3,a4,alpha)
    return theta1,theta2,theta3,a4,J
#准确率
def score(h,y):
    #将h和y变成1列
    h_=np.argmax(h,axis=1)
    y_=np.argmax(y,axis=1)
    return np.mean(y_==h_)


if __name__ == '__main__':
    #加载数据
    x=np.loadtxt('imgX.txt',delimiter=',')
    y=np.loadtxt('labely.txt',delimiter=',')
    #将标签的10换成0
    y[y==10]=0
    #特征缩放
    miu=np.mean(x)
    sigma=np.std(x)
    x=(x-miu)/sigma
    #拼接X
    X=np.c_[np.ones(len(x)),x]
    #洗牌
    np.random.seed(66)
    a=np.random.permutation(len(x))
    X=X[a]
    y=y[a]
    #标签的独热处理
    y_onehot=np.zeros((len(x),10))
    print(y_onehot)
    for i in range(len(x)):
        y_onehot[i,int(y[i])]=1
    print(y_onehot)
    # b=OneHotEncoder(categories='auto')
    # y_onehot=b.fit_transform(y.reshape(-1,1)).toarray()
    # print(y_onehot)
    #切分训练集和测试集
    num=int(0.7*len(x))
    train_x,test_x=np.split(X,[num,])
    train_y,test_y=np.split(y_onehot,[num,])
    #训练模型
    #a4：训练集的预测值
    theta1, theta2, theta3, a4, J=grad(train_x,train_y)
    plt.plot(J)
    plt.show()
    #输出训练集准确率
    print(score(a4,train_y))
    #计算测试集的预测值
    _,_,test_h=FP(test_x,theta1,theta2,theta3)
    #输出测试集精度
    print(score(test_h,test_y))
    #换出手写体识别的数字图像
    #x[666]:切出第666个样本/特征
    #reshape(20,20)：将特征变为20*20的数组
    a=x[666].reshape(20,20)
    #将数组传入画图方法，画图
    plt.imshow(a)
    plt.show()